package com.rpay.service.user.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.googlecode.jsonrpc4j.spring.rest.JsonRpcRestClient;
import com.rpay.common.dto.user.*;
import com.rpay.common.dto.userWallet.UserWalletDTO;
import com.rpay.common.dto.wallet.WalletDTO;
import com.rpay.common.enums.LevelEnum;
import com.rpay.common.enums.PcsResultCode;
import com.rpay.common.exception.PcsRunTimeException;
import com.rpay.common.exception.UserException;
import com.rpay.common.util.DateUtil;
import com.rpay.common.vo.RippleResult;
import com.rpay.dao.mapper.UserTradeDOMapper;
import com.rpay.dao.user.UserTradeDao;
import com.rpay.model.trade.TradeCacheDO;
import com.rpay.model.user.UserTradeDO;
import com.rpay.model.userWallet.UserWalletDO;
import com.rpay.service.trade.ITradeCacheService;
import com.rpay.service.user.IUserRippleService;
import com.rpay.service.user.IUserService;
import com.rpay.service.user.IUserTradeService;
import com.rpay.service.userWallet.IUserWalletService;
import com.rpay.service.wallet.IWalletService;
import org.apache.ibatis.session.RowBounds;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Service
public class UserTradeServiceImpl implements IUserTradeService {
    @Autowired
    private UserTradeDao userTradeDao;
    @Autowired
    UserTradeDOMapper userTradeDOMapper;
    @Autowired
    private IUserService userService;

    @Autowired
    private ITradeCacheService tradeCacheService;

    @Autowired
    private IUserWalletService userWalletService;

    @Autowired
    private IWalletService walletService;

    @Autowired
    private RestTemplate restTemplate;

    // 瑞波服务地址
    @Value("${ripple.url}")
    private String rippleUrl;

    // 汇率地址
    @Value("${ripple.rateUrl}")
    private String rateUrl;

    private ByteArrayOutputStream byteArrayOutputStream;


    @Override
    public PageInfo<Map> getDataList(Map map, RowBounds rowBounds) {
        return (PageInfo<Map>) userTradeDao.selectUserTradeList(map, rowBounds);
    }


    public Map getUserTradeDataList(Map param, RowBounds rowBounds) {
        List<UserTradeDTO> result = userTradeDOMapper.selectUserTradeList(param, rowBounds);
        int totalRow = userTradeDOMapper.selectUserTradeCount(param);
        Map hashMap = new HashMap();
        hashMap.put("page", (int) Math.floor(rowBounds.getOffset() / rowBounds.getLimit()) + 1);
        hashMap.put("pageSize", rowBounds.getLimit());
        hashMap.put("totalRow", totalRow);
        hashMap.put("list", result);
        return hashMap;
    }

    public Map getXRPInOutDataList(Map param, RowBounds rowBounds) {
        List<UserTradeDTO> result = userTradeDOMapper.selectInAndOutTradeList(param, rowBounds);
        int totalRow = userTradeDOMapper.selectInAndOutTradeCount(param);
        Map hashMap = new HashMap();
        hashMap.put("page", (int) Math.floor(rowBounds.getOffset() / rowBounds.getLimit()) + 1);
        hashMap.put("pageSize", rowBounds.getLimit());
        hashMap.put("totalRow", totalRow);
        hashMap.put("list", result);
        return hashMap;
    }

    public int updateTradeStatus(UserTradeDO userTradeDO) {
        return userTradeDOMapper.updateByPrimaryKeySelective(userTradeDO);
//        return userTradeDao.updateUserTradeStatus(userTradeDO);

    }

    @Override
    public PageInfo<Map> getSelfTrades(Map map, RowBounds rowBounds) {
        return (PageInfo<Map>) userTradeDao.selectSelfTradeList(map, rowBounds);
    }

    @Override
    public List<UserTradeDO> getTradesByExecTime() {
        return userTradeDao.selectUserTradesByExecTime();
    }

    @Override
    public boolean saveUserTrade(UserTradeDTO userTradeDTO) {
        userTradeDao.insert(convertDTOToDO(userTradeDTO));
        return true;
    }

    public List<UserTradeDO> getPendingAuditList() {
        return userTradeDao.getPendingAuditTrade();
    }


    @Override
    public boolean investConfirm(int tradeId) {
        UserTradeDO userTradeDO = userTradeDao.findById(tradeId);
        UserDTO userDTO = userService.getUserById(userTradeDO.getUserId());
        if (userDTO == null) {
            throw new UserException(PcsResultCode.USER_INVALID);
        }
        // 确认充值需要添加用户的瑞波币余额
        BigDecimal userRippleAmount = userDTO.getRippleAmount();
        BigDecimal tradeRippleAmount = userTradeDO.getRippleAmount();
        userDTO.setRippleAmount(userRippleAmount.add(tradeRippleAmount));
        userService.updateUser(userDTO);

        // 把状态设置成完成
        userTradeDO.setStatus("END");
        userTradeDao.patch(userTradeDO);
        return true;
    }

    @Override
    public void withdraw(UserTradeDTO userTradeDTO, String rateStr) {
        UserDTO userDTO = userService.getUserById(userTradeDTO.getUserId());
        if (userDTO == null) {
            throw new UserException(PcsResultCode.USER_INVALID);
        }

        // 提现限制
        if (!hasEnoughQuota(userTradeDTO, userDTO)) {
            throw new UserException(PcsResultCode.ERROR_OPERATE, "今日没有足够的提现量!");
        } else if (!hasEnoughBuyAmount(userTradeDTO, userDTO)) { // 有足够的额提现量
            throw new UserException(PcsResultCode.ERROR_OPERATE, "余额不足!");
        } else if (!hasEnoughTime(userTradeDTO)) {
            throw new UserException(PcsResultCode.ERROR_OPERATE, "超过了每日提现次数!");
        } else {
            // 添加提现记录
            BigDecimal userRippleAmount = userDTO.getRippleAmount();
            BigDecimal userBuyAmount = userDTO.getBuyAmount();
            UserTradeDO userTradeDO = convertDTOToDO(userTradeDTO);

            BigDecimal rate = userTradeDO.getRate();

            int day = 0;
            if (rateStr.equals("0.1")) {
                day = 1;
            } else if (rateStr.equals("0.05")) {
                day = 3;
            } else if (rateStr.equals("0.025")) {
                day = 7;
            }
            Calendar currentDate = new GregorianCalendar();
            Date deadline = DateUtil.addWeekday(currentDate.getTime(), day);
            String deadlineDate = DateUtil.formatDate(deadline);
            userTradeDO.setExpiry_date(deadlineDate);

            BigDecimal amount = userTradeDO.getRippleAmount();
            BigDecimal cost = amount.multiply(rate);
            userTradeDO.setRippleAmount(amount.subtract(cost));
            userTradeDO.setCost(cost);
            System.out.println(userTradeDO);
            userTradeDao.insert(userTradeDO);
            // 从账户余额中扣除
            userDTO.setRippleAmount(userRippleAmount.subtract(amount));
            userDTO.setBuyAmount(userBuyAmount.subtract(amount));
            userService.updateUser(userDTO);
        }
    }

    // 提现的时候扣除账户余额，确认的时候无需重新设置账户余额
    @Override
    public boolean withdrawConfirm(int tradeId, int walletId) {
        UserTradeDO userTradeDO = userTradeDao.findById(tradeId);
        if (userTradeDO == null) {
            throw new UserException(PcsResultCode.PARAM_INVALID, "此交易申请不存在！");
        }
        WalletDTO walletDTO = walletService.findById(walletId);

        if (walletDTO == null) {
            throw new UserException(PcsResultCode.PARAM_INVALID, "系统提现钱包不存在！");
        }
        UserWalletDTO userWalletDTO = userWalletService.findById(userTradeDO.getDestination_wallet_id());
        if (userWalletDTO == null) {
            throw new UserException(PcsResultCode.PARAM_INVALID, "用户提现钱包不存在！");
        }
        System.out.println(userWalletDTO);
        System.out.println(userTradeDO.getDestination_wallet_id());
        int actualAmount = getActualAmount(userTradeDO);
        TxResponseDTO txResponseDTO = paymentRipple(userWalletDTO, walletDTO, userTradeDO.getUserId(), actualAmount);
        if (txResponseDTO != null) {
            // 系统转账成功，提现完成，设置交易记录。
            BigDecimal amount = userTradeDO.getRippleAmount();
            BigDecimal cost = amount.multiply(userTradeDO.getRate());
            userTradeDO.setTx_hash(txResponseDTO.getHash());
            userTradeDO.setFee(txResponseDTO.getFee());
            userTradeDO.setStatus("END");
            userTradeDO.setCost(cost);
            userTradeDao.patch(userTradeDO);
            return true;
        }
        return false;
    }

    // 提现的时候扣除账户余额，确认的时候无需重新设置账户余额
    @Override
    public boolean manualWithDrawConfirm(int tradeId) {
        UserTradeDO userTradeDO = userTradeDao.findById(tradeId);
        if (userTradeDO == null) {
            throw new UserException(PcsResultCode.PARAM_INVALID, "此交易申请不存在！");
        }
        BigDecimal amount = userTradeDO.getRippleAmount();
        BigDecimal cost = amount.multiply(userTradeDO.getRate());
        userTradeDO.setStatus("END");
        userTradeDO.setCost(cost);
        userTradeDao.patch(userTradeDO);
        return true;
    }


    @Override
    public void conversion(UserTradeDTO userTradeDTO) {
        UserDTO userDTO = userService.getUserById(userTradeDTO.getUserId());

        if (userDTO == null) {
            throw new UserException(PcsResultCode.USER_INVALID);
        }
        if (hasEnoughRPayAmount(userTradeDTO, userDTO)) {
            // 减去用户RPAY余额
            BigDecimal userRPayAmount = userDTO.getRpayAmount();
            BigDecimal userRippleAmount = userDTO.getRippleAmount();
            BigDecimal userBuyAmount = userDTO.getBuyAmount(); // 可提现
            System.out.println(userBuyAmount);
            BigDecimal rPayAmount = userTradeDTO.getRpayAmount();


            // 设置费用,添加转换记录
            BigDecimal rate = new BigDecimal("0.05");
            BigDecimal cost = rPayAmount.multiply(rate);// 转换费用
            BigDecimal reverseRate = getReverseRate();
            BigDecimal rippleAmount = rPayAmount.subtract(cost).multiply(reverseRate); // 获得的瑞波币

            Calendar currentDate = new GregorianCalendar();
            Date deadline = DateUtil.addWeekday(currentDate.getTime(), 1);
            String deadlineDate = DateUtil.formatDate(deadline);
            userTradeDTO.setRate(rate);
            userTradeDTO.setRippleAmount(rippleAmount); // 转到可提现余额
            userTradeDTO.setCost(cost);
            userTradeDTO.setExpiry_date(deadlineDate);
            userTradeDao.insert(convertDTOToDO(userTradeDTO));

            userDTO.setRpayAmount(userRPayAmount.subtract(rPayAmount));
            userDTO.setRippleAmount(userRippleAmount.add(rippleAmount));
            userDTO.setBuyAmount(userBuyAmount.add(rippleAmount));
            userService.updateUser(userDTO);

        } else {
            throw new UserException(PcsResultCode.ERROR_OPERATE, "余额不足!");
        }

    }

    @Override
    public boolean conversionConfirm(int tradeId) {

        // 修改账单状态
        UserTradeDO userTradeDO = userTradeDao.findById(tradeId);

        if (userTradeDO == null) {
            throw new UserException(PcsResultCode.PARAM_INVALID, "此交易申请不存在！");
        }

        UserDTO userDTO = userService.getUserById(userTradeDO.getUserId());
        if (userDTO == null) {
            throw new UserException(PcsResultCode.USER_INVALID);
        }

        // 更新交易记录中的费用瑞波数量
        BigDecimal rate = userTradeDO.getRate();// 转换费率
        BigDecimal rPayAmount = userTradeDO.getRpayAmount();
        BigDecimal cost = rPayAmount.multiply(rate);// 转换费用
        BigDecimal reverseRate = getReverseRate();
        BigDecimal rippleAmount = rPayAmount.subtract(cost).multiply(reverseRate); // 获得的瑞波币
        userTradeDO.setRippleAmount(rippleAmount);
        userTradeDO.setCost(cost);
        userTradeDao.patch(userTradeDO);

        // 更新用户的瑞波数量，等价转换为瑞波币，并减去费用
        BigDecimal userRippleAmount = userDTO.getRippleAmount();
        userDTO.setRippleAmount(userRippleAmount.add(rippleAmount));
        userService.updateUser(userDTO);
        return true;
    }

    @Override
    public void transfer(int userId, int destUserId, String amountStr) {
        BigDecimal amount = new BigDecimal(amountStr);
        UserDTO userDTO = userService.getUserById(userId);
        UserDTO destUserDTO = userService.getUserById(destUserId);
        if (userDTO.getId() == null) {
            throw new UserException(PcsResultCode.USER_INVALID);
        } else if (destUserDTO.getId() == null) {
            throw new UserException(PcsResultCode.USER_INVALID);
        } else if (!userDTO.getTeamKey().equals(destUserDTO.getTeamKey())) {
            throw new UserException(PcsResultCode.USER_INVALID, "必须同一伞下的用户才能想住转换");
        }

        if (hasEnoughRippleAmount(amount, userDTO)) {
//            BigDecimal rate = new BigDecimal("0.05");
            BigDecimal rate = new BigDecimal("0");
            BigDecimal cost = amount.multiply(rate);// 互转费用
//            BigDecimal destAmount = amount.subtract(cost); // 获得的瑞波币
            // 提币不需要手续费了
            BigDecimal destAmount = amount; // 获得的瑞波币

            // 当前交易记录
            UserTradeDO userTradeDO = new UserTradeDO();
            userTradeDO.setCost(cost);
            userTradeDO.setRate(rate);
            userTradeDO.setRippleAmount(amount);
            userTradeDO.setType("TRANSFER");
            userTradeDO.setStatus("END");
            userTradeDO.setUserId(userId);
            userTradeDao.insert(userTradeDO);

//            // 目标用户交易记录
//            UserTradeDO destUserTradeDO = new UserTradeDO();
//            destUserTradeDO.setRippleAmount(destAmount);
//            destUserTradeDO.setType("TRANSFER");
//            destUserTradeDO.setStatus("END");
//            destUserTradeDO.setUserId(destUserId);
//            userTradeDao.insert(destUserTradeDO);

            // 减去用户XRP余额
            BigDecimal userRippleAmount = userDTO.getRippleAmount();
            userDTO.setRippleAmount(userRippleAmount.subtract(amount));

            BigDecimal buyAmount = userDTO.getBuyAmount();
            int res = buyAmount.compareTo(amount);
            if (res < 0) { // 当可提现余额不足时，设置为0
                userDTO.setBuyAmount(new BigDecimal("0"));
            } else {
                userDTO.setBuyAmount(buyAmount.subtract(amount));
            }

            userService.updateUser(userDTO);

            // 增加目标的XRP
            BigDecimal destUserRippleAmount = destUserDTO.getRippleAmount();
            destUserDTO.setRippleAmount(destUserRippleAmount.add(destAmount));
            userService.updateUser(destUserDTO);
        } else {
            throw new UserException(PcsResultCode.ERROR_OPERATE, "余额不足!");
        }
    }

    @Override
    public BigDecimal getRate() {
        BigDecimal rate = getReverseRate();
        if (rate != null && !rate.equals(BigDecimal.ZERO)) {
            return new BigDecimal(1).divide(rate, 4, BigDecimal.ROUND_HALF_UP);
        }
        return null;
    }

    private BigDecimal getReverseRate() {
        // 当没有获取成功时取上一次存在数据录里的
        try {
            ResponseEntity<String> response = restTemplate
                    .getForEntity(rateUrl, String.class);
            String body = response.getBody();
            JSONObject rst = new JSONObject(body);
            if (rst.get("result").equals("success")) {
                String rateString = rst.getString("rate");
                BigDecimal rate = new BigDecimal(rateString);
                TradeCacheDO tradeCacheDO = new TradeCacheDO();
                tradeCacheDO.setRate(rate);
                tradeCacheService.save(tradeCacheDO);
                return rate;
            } else {
                TradeCacheDO tradeCacheDO = tradeCacheService.findOne();
                return tradeCacheDO.getRate();
            }
        } catch (Exception e) {
            TradeCacheDO tradeCacheDO = tradeCacheService.findOne();
            return tradeCacheDO.getRate();
        }
    }

    // 奖金每日一提，1-5级会员每日封顶2万元，股东每日提现封顶5万元。
    private boolean hasEnoughQuota(UserTradeDTO userTradeDTO, UserDTO userDTO) {
        BigDecimal userRippleAmount = userTradeDTO.getRippleAmount();
        BigDecimal sixQuota = new BigDecimal(50000);
        BigDecimal quota = new BigDecimal(20000);
        System.out.println(userRippleAmount);
        int res;
        int level = userDTO.getLevel();
        if (LevelEnum.SIX.getLevel() == level) {
            res = userRippleAmount.compareTo(sixQuota);
        } else {
            res = userRippleAmount.compareTo(quota);
        }
        System.out.println(res);
        return res < 0;
    }

    private boolean hasEnoughBuyAmount(UserTradeDTO userTradeDTO, UserDTO userDTO) {
        BigDecimal rippleAmount = userTradeDTO.getRippleAmount();
        BigDecimal userBuyAmount = userDTO.getBuyAmount();
        int res = rippleAmount.compareTo(userBuyAmount);
        return res <= 0;
    }

    private boolean hasEnoughRippleAmount(BigDecimal rippleAmount, UserDTO userDTO) {
        BigDecimal userRippleAmount = userDTO.getRippleAmount();
        int res = rippleAmount.compareTo(userRippleAmount);
        int res1 = rippleAmount.compareTo(new BigDecimal("0"));
        return res <= 0 && res1 > 0;
    }

    private boolean hasEnoughRPayAmount(UserTradeDTO userTradeDTO, UserDTO userDTO) {
        BigDecimal rPayAmount = userTradeDTO.getRpayAmount();
        BigDecimal userRPayAmount = userDTO.getRpayAmount();
        int res = rPayAmount.compareTo(userRPayAmount);
        int res1 = rPayAmount.compareTo(new BigDecimal("0"));
        return res <= 0 && res1 > 0;
    }

    private boolean hasEnoughTime(UserTradeDTO userTradeDTO) {
        Map param = new HashMap();
        param.put("type", "WITHDRAW");
        param.put("userId", userTradeDTO.getUserId());
        Calendar currentDate = new GregorianCalendar();
        currentDate.add(Calendar.DATE, -1);    //得到前一天


        String startDate = DateUtil.formatDate(currentDate.getTime());
        param.put("startDate", startDate);
        System.out.println(param);
        int count = userTradeDao.getUserTradeCount(param);
        System.out.println(count);
        return count == 0;
    }

    private TxResponseDTO paymentRipple(UserWalletDTO userWalletDTO, WalletDTO walletDTO, Integer userId, int actualAmount) {
        URL url = null;
        try {
            url = new URL(rippleUrl);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        JsonRpcRestClient client = new JsonRpcRestClient(url);
        HashMap<String, Object> mainMap = new HashMap<>();

        mainMap.put("offline", false);
        mainMap.put("secret", walletDTO.getSecretKey().trim());

        Map<String, String> subMap = new HashMap<>();
        subMap.put("Account", walletDTO.getWalletAddr().trim()); // amount will be deducted from this account
        subMap.put("Amount", String.valueOf(actualAmount)); // in drops
        subMap.put("Destination", userWalletDTO.getWalletAddr().trim()); // receiver address
        subMap.put("DestinationTag", userWalletDTO.getWalletTag()); // receiver tag
        subMap.put("TransactionType", "Payment"); // since we are making a  payment request
        mainMap.put("tx_json", subMap);

        List<Map<String, Object>> params = new ArrayList<Map<String, Object>>();
        params.add(mainMap);

        try {
            RippleResult rst = client.invoke("submit", params, RippleResult.class);
            if (rst.getStatus().equals("success")) {
                return rst.getTx_json();
            } else { // 转账失败处理
                throw new UserException(PcsResultCode.ERROR_OPERATE, "转账失败，请重试");
            }
        } catch (Throwable e) {
            System.out.println(e.getMessage());
            throw new UserException(PcsResultCode.ERROR_OPERATE, "转账失败，请重试");
        }
    }

    // 计算实际提现金额，扣除手续费
    private int getActualAmount(UserTradeDO userTradeDO) {
        BigDecimal amount = userTradeDO.getRippleAmount();
        BigDecimal big = new BigDecimal("1000000");
        return amount.multiply(big).intValueExact();
    }


    private UserTradeDO convertDTOToDO(UserTradeDTO userTradeDTO) {
        UserTradeDO userTradeDO = null;
        if (userTradeDTO != null) {
            userTradeDO = new UserTradeDO();
            if (userTradeDTO.getId() != null) {
                userTradeDO.setId(userTradeDO.getId());
            }
            userTradeDO.setUserId(userTradeDTO.getUserId());
            userTradeDO.setType(userTradeDTO.getType());
            userTradeDO.setRippleAmount(userTradeDTO.getRippleAmount());
            userTradeDO.setRpayAmount(userTradeDTO.getRpayAmount());
            userTradeDO.setPrice(userTradeDTO.getPrice());
            userTradeDO.setStatus(userTradeDTO.getStatus());
            userTradeDO.setRate(userTradeDTO.getRate());
            userTradeDO.setCost(userTradeDTO.getCost());
            userTradeDO.setTx_hash(userTradeDTO.getTx_hash());
            userTradeDO.setInvest_wallet(userTradeDTO.getInvest_wallet());
            userTradeDO.setExpiry_date(userTradeDTO.getExpiry_date());
            userTradeDO.setWithdraw_wallet_id(userTradeDTO.getWithdraw_wallet_id());
            userTradeDO.setDestination_wallet_id(userTradeDTO.getDestination_wallet_id());
        }

        return userTradeDO;
    }

    private BigDecimal getRPayCost(BigDecimal amount) {
        return amount.multiply(new BigDecimal("0.05"));
    }

    private JsonNode readJSON(JsonRpcRestClient client, ByteArrayOutputStream byteArrayOutputStream) throws IOException {
        return client.getObjectMapper().readTree(byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()));
    }
}
